// Design developped by Mina Kaiser 
// Sec 2  - CSE14

#include <iostream>
#include <string>
#include <vector>
#include <sstream>

// Main Class
class DesignByContractException : public std::logic_error  
	// logic_error : This class defines the type of objects thrown as exceptions to report errors in the internal logical of the program. These are theoretically preventable.
{
protected:
	// Construction - only for derived classes
	DesignByContractException(const std::string& file,int line,const std::string& what = std::string()); 
	// Constructor takes the following arguments : The file which have an error , the line  and the error 

public:
	// Queries

	// Exception error prefix
	std::string errorPrefix() const;

	const std::string& file() const;
	int line() const;

	// Source, location and description of exception
	operator std::string() const;

protected:
	// Commands
	void setErrorPrefix(const std::string& errorPrefix);

private:
	std::string file_;
	int line_;
	std::string errorPrefix_;
};

// Definitions 

DesignByContractException::DesignByContractException( const std::string& file, int line, const std::string& what /*= std::string()*/ ) 
	: std::logic_error(what),  file_(file), line_(line),  errorPrefix_("DesignByContract failed")
{
}

std::string DesignByContractException::errorPrefix() const
{
	return errorPrefix_;
}

const std::string& DesignByContractException::file() const
{
	return file_;
}

int DesignByContractException::line() const
{
	return line_;
}

DesignByContractException::operator std::string() const
{
	std::ostringstream stream;
	stream << "File: " << file() << "\nLine: " << line() << "\n" << errorPrefix() << ". " << what() << "\n";
	return stream.str();
}

void DesignByContractException::setErrorPrefix( const std::string& errorPrefix )
{
	errorPrefix_ = errorPrefix;
}

// INVARIANT Class
class InvariantException : public DesignByContractException
{
public:
	InvariantException(const std::string& file,
		int      line,
		const std::string& what = std::string());
};

// Definitions 
InvariantException::InvariantException( const std::string& file, int line, const std::string& what /*= std::string()*/ ) 
	: DesignByContractException(file, line, what)
{
	setErrorPrefix("Invariant failed");
}

// POSTCONDITION Class
class PostconditionException : public DesignByContractException
{
public:
	PostconditionException(const std::string& file,
		int      line,
		const std::string& what = std::string());
};

// Definitions 
PostconditionException::PostconditionException( const std::string& file, int line, const std::string& what /*= std::string()*/ ) 
	: DesignByContractException(file, line, what)
{
	setErrorPrefix("Postcondition failed");
}

// PRECONDITION Class
class PreconditionException : public DesignByContractException
{
public:
	PreconditionException(const std::string& file,
		int      line,
		const std::string& what = std::string());
};

//Definitions 
PreconditionException::PreconditionException( const std::string& file, int line, const std::string& what /*= std::string()*/ ) 
	: DesignByContractException(file, line, what)
{
	setErrorPrefix("Precondition failed");
}


//Macros Definitions 
#if defined(_DEBUG)    // Works only in the Debug mode	

#define ENSURE0(assertion)			if (!(assertion)) throw Ensure(__FILE__, __LINE__)
#define ENSURE(assertion, what)		if (!(assertion)) throw Ensure(__FILE__, __LINE__, what)

#define REQUIRE0(assertion)			if (!(assertion)) throw Require(__FILE__, __LINE__)
#define REQUIRE(assertion, what)	if (!(assertion)) throw Require(__FILE__, __LINE__, what)

#define INVARIANT0(assertion)		if (!(assertion)) throw Invariant(__FILE__, __LINE__)
#define INVARIANT(assertion, what)	if (!(assertion)) throw Invariant(__FILE__, __LINE__, what)

#else // Not DESIGN_BY_CONTRACT - disable everything
#define ENSURE0(assertion)				((void)0)
#define ENSURE(assertion, what)			((void)0)

#define REQUIRE0(assertion)				((void)0)
#define REQUIRE(assertion, what)		((void)0)

#define INVARIANT0(assertion)			((void)0)
#define INVARIANT(assertion, what)		((void)0)
#endif // DESIGN_BY_CONTRACT

typedef PostconditionException	Ensure;
typedef PreconditionException	Require;
typedef InvariantException		Invariant;

	